package com.vhly.ddxdecompiler.dexreader.file.mixeditem;

import com.vhly.classfile.*;
import com.vhly.classfile.util.CPTool;
import com.vhly.ddxdecompiler.dexreader.file.DexFile;
import com.vhly.ddxdecompiler.dexreader.file.TypeIdsSection;
import com.vhly.ddxdecompiler.dexreader.file.constant.CstConstant;
import com.vhly.ddxdecompiler.dexreader.io.DexInputStream;
import com.vhly.ddxdecompiler.dexreader.util.StringUtil;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Vector;

/**
 * Created by IntelliJ IDEA.
 * User: vhly
 * Date: 2009-10-15
 * Time: 12:43:31
 *
 * @author vhly[FR]
 */
public final class ClassDefItem extends IndexedItem {

    int classIdx;
    int accessFlags;
    int superIdx;
    int interOff;
    int sourceFileIdx;
    int annoOff;
    int dataOff;
    int staticValuesOff;

    private TypeIdItem thisItem;
    private TypeIdItem superItem;
    private StringIdItem sourceFile;
    /**
     * 接口列表
     */
    private TypeListItem interfaces;
    private EncodedArrayItem staticValuesItem;
    private AnnotationsDirectoryItem annotationsDirectory;
    private ClassDataItem classData;

    private DexFile dex;

    public ClassDefItem(DexFile dexFile) {
        dex = dexFile;
    }

    public void read(DexInputStream din) throws IOException {
        classIdx = din.readInt();    // TypeIdsSection 中 thisClass 的索引
        accessFlags = din.readInt();
        superIdx = din.readInt();
        interOff = din.readInt();
        sourceFileIdx = din.readInt();
        annoOff = din.readInt();
        dataOff = din.readInt();
        staticValuesOff = din.readInt();
    }

    public void write(DataOutputStream dout) throws IOException {
    }


    public void reloc(ByteArrayInputStream bin, int offset) throws IOException {
        TypeIdsSection types = dex.getTypeIds();
        thisItem = types.indexOf(classIdx);
        superItem = types.indexOf(superIdx);
        sourceFile = dex.getStringIds().indexOf(sourceFileIdx);

        // TODO 显示类文件信息
        if(thisItem != null){
            String tc = thisItem.item.getDataItem().value;
            System.out.println("[Debug] class " + tc);
        }
        
        int off = interOff;
        if (off != 0) {
            interfaces = new TypeListItem(dex);
            bin.reset();
            bin.skip(off - offset);
            DataInputStream din = new DataInputStream(bin);
            DexInputStream dxin = new DexInputStream(din, 1);
            dxin.setOffset(off);
            interfaces.read(dxin);
            dxin.close();
            din.close();
        }

        off = annoOff;
        if (off != 0) {
            annotationsDirectory = new AnnotationsDirectoryItem(dex);
            bin.reset();
            bin.skip(off - offset);
            DataInputStream din = new DataInputStream(bin);
            DexInputStream dxin = new DexInputStream(din, 1);
            dxin.setOffset(off);
            annotationsDirectory.read(dxin);
            annotationsDirectory.reloc(bin, offset);
            dxin.close();
            din.close();
        }

        off = dataOff;
        if (off != 0) {
            classData = new ClassDataItem(dex);
            bin.reset();
            bin.skip(off - offset);
            DataInputStream din = new DataInputStream(bin);
            DexInputStream dxin = new DexInputStream(din, 1);
            dxin.setOffset(off);
            classData.read(dxin);
            classData.reloc(bin, offset);
            dxin.close();
            din.close();
        }

        off = staticValuesOff;    // TODO Test this.
        if (off != 0) {
            staticValuesItem = new EncodedArrayItem(dex);
            bin.reset();
            bin.skip(off - offset);
            DataInputStream din = new DataInputStream(bin);
            DexInputStream dxin = new DexInputStream(din, 1);
            dxin.setOffset(off);
            staticValuesItem.read(dxin);
            staticValuesItem.reloc(bin, offset);
            dxin.close();
            din.close();
        }
    }

    /**
     * ClassDefItem reverse to JavaClass Object,
     * If conver to JavaClass, We can decompile it with Any Java Decompiler.
     * @return JavaClass
     * @throws IOException IOE
     */
    public JavaClass reverseClass() throws IOException {
//        ByteArrayOutputStream bout = new ByteArrayOutputStream();
//        DataOutputStream dout = new DataOutputStream(bout);
        JavaClass clazz = new JavaClass();
        clazz.setMajVersion(49);
        clazz.setMinVersion(0);
        clazz.setAccess(AccessFlag.ACC_PUBLIC);
        String thisName = thisItem.item.toString();
        clazz.setThisName(StringUtil.removeClassModify(thisName));
        String superName = superItem.item.toString();
        clazz.setSuperName(StringUtil.removeClassModify(superName));
        if (interfaces != null) {
            Vector<TypeIdItem> infs = interfaces.items;
            int size = infs.size();
            TypeIdItem type;
            for (int i = 0; i < size; i++) {
                type = infs.elementAt(i);
                String iname = type.item.toString();
                clazz.addInterfaceClass(StringUtil.removeClassModify(iname));
            }
        }
        ArrayList<EncodeField> sfs = classData.staticFields;
        if(sfs != null){
            // TODO process Class Static fields
            if(staticValuesItem != null){
                int size = staticValuesItem.size();
                if(size > 0){
                    size = sfs.size();
                    EncodeField ef;
                    Object obj;
                    for(int i = 0;i<size;i++){
                        ef = sfs.get(i);
                        obj = staticValuesItem.elementAt(i);
                        if(obj != null){
                            ef.constant = (CstConstant) obj;
                        }
                    }
                }
            }
        }
        // TODO This is very Important!!
        classData.reverseClass(clazz);

        // TODO process File annotations Info
        if(annotationsDirectory != null){
            annotationsDirectory.reverseClass(clazz);
        }

        if(sourceFile != null){
            String sf = sourceFile.toString();
            ConstantPool pool = clazz.getCP();
            int sfi = CPTool.addUTF8(pool, sf);
            int ni = CPTool.addUTF8(pool,"SourceFile");
            SourceFile attr = new SourceFile(ni,sfi);
            int si = clazz.addAttribute(attr);
            clazz.setSourceIndex(si);
        }

//        dout.close();
//        bout.close();
        return clazz;
    }

    public TypeIdItem getSuperItem() {
        return superItem;
    }

    public TypeIdItem getThisItem() {
        return thisItem;
    }

    public ClassDataItem getClassData() {
        return classData;
    }
}
